/*
 * @ProjectName: 编程学习
 * @Copyright:   2019 HangZhou Yi Dev, Ltd. All Right Reserved.
 * @address:     https://yiyuery.github.io/
 * @date:        2019/5/20 20:57
 * @email:       xiazhaoyang@live.com
 * @description: 本内容仅限于编程技术学习使用，转发请注明出处.
 */
package com.example.component.gallery.service.bing.impl;

import com.example.common.api.exception.BusinessException;
import com.example.common.api.exception.code.ApiErrorCode;
import com.example.common.api.service.jpa.BaseJpaServiceImpl;
import com.example.common.api.service.jpa.BaseRepository;
import com.example.common.api.validation.ValidateUtil;
import com.example.common.io.constant.FileSuffixConstant;
import com.example.common.spring.util.RestClient;
import com.example.common.util.CapDateTimeUtil;
import com.example.component.gallery.common.base.ImageCollector;
import com.example.component.gallery.common.constant.ImageSuffixConstant;
import com.example.component.gallery.common.enums.ImageCompressionLevel;
import com.example.component.gallery.common.util.ImageHelper;
import com.example.component.gallery.core.dao.ImageStorageRepository;
import com.example.component.gallery.core.model.ImageStorage;
import com.example.component.gallery.service.bing.dto.BingImageResponse;
import com.example.component.gallery.service.bing.dto.CompressionImageDTO;
import com.example.component.gallery.service.bing.dto.ImagesBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.core.io.Resource;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 *
 * </p>
 *
 * @author Yi
 * @version v1.0.0
 * @date 2019-07-21 08:55
 * @modificationHistory=========================逻辑或功能性重大变更记录
 * @modify By: {修改人} 2019-07-21
 * @modify reason: {方法名}:{原因}
 * ...
 */
@Service
@Slf4j
public class BingImageCollector extends BaseJpaServiceImpl<ImageStorage, Long> implements ImageCollector {

    @javax.annotation.Resource
    private ImageStorageRepository repository;

    /**
     * 收集
     */
    @Override
    public void collect() {
        String baseUrl = "https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1";
        String bingHost = "https://cn.bing.com";
        //1. 获取图片地址
        ResponseEntity<BingImageResponse> bingResp = RestClient.getInstance().exchange(baseUrl, HttpMethod.GET, (HttpEntity<?>) null, BingImageResponse.class);
        ValidateUtil.requireSatisfyThrowException(!HttpStatus.OK.equals(bingResp.getStatusCode()), "load bing image failed!");
        ValidateUtil.requireSatisfyThrowException(CollectionUtils.isEmpty(Objects.requireNonNull(bingResp.getBody()).getImages()), "bing image list is empty!");
        ImagesBean image = Objects.requireNonNull(bingResp.getBody()).getImages().get(0);
        image.setCopyright(image.getCopyright().substring(0, image.getCopyright().indexOf("(©")));
        //2. 获取图片流 & 保存
        ResponseEntity<Resource> remoteResource = RestClient.getInstance().exchange(bingHost + image.getUrl(), HttpMethod.GET, (HttpEntity<?>) null, Resource.class);
        ValidateUtil.requireSatisfyThrowException(!HttpStatus.OK.equals(remoteResource.getStatusCode()), "load bing image resource failed!");
        batchSaveImage(image, remoteResource, Arrays.asList(
                CompressionImageDTO.builder()
                        .compressionLevel(ImageCompressionLevel.IMAGE_COMPRESSION_LEVEL_ORIGINAL)
                        .imageName(image.getLocalName(ImageSuffixConstant.IMAGE_SUFFIX_PNG)).build(),
                CompressionImageDTO.builder()
                        .compressionLevel(ImageCompressionLevel.IMAGE_COMPRESSION_LEVEL_1200_800)
                        .imageName(image.getLocalName(ImageSuffixConstant.IMAGE_SPLIT_CHARACTER,
                                ImageCompressionLevel.IMAGE_COMPRESSION_LEVEL_1200_800.getDimension(),
                                ImageSuffixConstant.IMAGE_SUFFIX_PNG)).build()));
    }

    /**
     * 批量保存图片
     *
     * @param image
     * @param remoteResource
     * @param compressionImageDTOS
     * @return
     */
    private void batchSaveImage(ImagesBean image, ResponseEntity<Resource> remoteResource, List<CompressionImageDTO> compressionImageDTOS) {
        ValidateUtil.requireNotSatisfyThrowException(CollectionUtils::isNotEmpty, compressionImageDTOS).forEach(p -> {
            saveImageToLocal(image,
                    p.getImageName(),
                    remoteResource,
                    p.getCompressionLevel());
        });
    }

    /**
     * 校验文件是否已经存在
     *
     * @param imageName
     */
    private void validateImageNameHasExist(String imageName) {
        //创建匹配器，即如何使用查询条件
        ExampleMatcher exMatcher = ExampleMatcher.matching()
                .withMatcher("imageName", ExampleMatcher.GenericPropertyMatchers.endsWith())
                .withMatcher("imageName", ExampleMatcher.GenericPropertyMatchers.startsWith());
        Example<ImageStorage> ex = Example.of(ImageStorage.builder()
                .imageName(imageName)
                .build(), exMatcher);
        if (exists(ex)) {
            throw new BusinessException(ApiErrorCode.DB_NOT_EXIST_ERROR.getCode());
        }
    }

    /**
     * 保存图片到本地
     *
     * @param image
     * @param imageName
     * @param remoteResource
     * @param compressionLevel
     */
    private void saveImageToLocal(ImagesBean image, String imageName, ResponseEntity<Resource> remoteResource, ImageCompressionLevel compressionLevel) {
        File imageDir = ImageHelper.IMAGE_LOCAL_CACHE_DIR;
        validateImageNameHasExist(imageName);
        File imagePath = Paths.get(imageDir.getAbsolutePath(), imageName).toFile();
        InputStream responseInputStream;
        try {
            responseInputStream = Objects.requireNonNull(remoteResource.getBody()).getInputStream();
            if (!imageDir.exists()) {
                imageDir.mkdirs();
            }
            BufferedImage bufferedImage = ImageIO.read(responseInputStream);
            //根据压缩级别进行压缩
            switch (compressionLevel) {
                case IMAGE_COMPRESSION_LEVEL_ORIGINAL:
                    ImageIO.write(bufferedImage, FileSuffixConstant.FILE_SUFFIX_IMAGE_PNG, imagePath);
                    break;
                case IMAGE_COMPRESSION_LEVEL_1200_800:
                    /**
                     * 绘制图像:
                     * - 构造一个类型为预定义图像类型之一的 BufferedImage
                     * - getScaledInstance表示创建此图像的缩放版本，返回一个新的缩放版本Image,按指定的width,height呈现图像
                     * - Image.SCALE_SMOOTH,选择图像平滑度比缩放速度具有更高优先级的图像缩放算法。
                     */
                    BufferedImage holder = new BufferedImage(compressionLevel.getWidth(), compressionLevel.getHeight(), BufferedImage.TYPE_INT_RGB);
                    holder.getGraphics().drawImage(bufferedImage.getScaledInstance(compressionLevel.getWidth(), compressionLevel.getHeight(), Image.SCALE_SMOOTH), 0, 0, null);
                    ImageIO.write(holder, FileSuffixConstant.FILE_SUFFIX_IMAGE_PNG, imagePath);
                    break;
                default:
                    break;
            }
        } catch (Throwable e) {
            throw new BusinessException(ApiErrorCode.INNER_LOGIC_ERROR.getCode());
        }
        //入库
        save(ImageStorage.builder()
                .imageName(imageName)
                .imageDescription(image.getCopyright())
                .localPath(imagePath.getAbsolutePath())
                .imageUrl(image.getUrl())
                .compression(compressionLevel.getLevel())
                .build());
    }

    /**
     * 获取Dao
     *
     * @return
     */
    @Override
    protected BaseRepository<ImageStorage, Long> getRepository() {
        return repository;
    }
}
